home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Info-Mac 3
/
Info_Mac_1994-01.iso
/
Development
/
General
/
GCC 1.37.1r15
/
GCC.MPW
/
aux-output.c
next >
Wrap
Text File
|
1993-01-04
|
49KB
|
1,746 lines
/* Subroutines for insn-output.c for Macintosh.
Several routines are taken from the generic 68k aux-output,
the rest of this file is original code.
Copyright (C) 1987 Free Software Foundation, Inc.
Copyright (C) 1989, 1990 Apple Computer, Inc.
This file is part of GNU CC.
GNU CC is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 1, or (at your option)
any later version.
GNU CC is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with GNU CC; see the file COPYING. If not, write to
the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
#include <stdio.h>
int globalize_this;
/* Size varies according to cmd option. */
int long_double_type_size;
/* Index into this array by (register number >> 3) to find the
smallest class which contains that register. */
enum reg_class regno_reg_class[]
= { DATA_REGS, ADDR_REGS, FP_REGS };
static rtx find_addr_reg ();
#include "tree.h"
rtx
function_arg( CUMULATIVE_ARGS *cum, enum machine_mode mode, int named )
{
int i;
if ( cum->p == NULL )
return( 0 );
if(cum->p->params[cum->count]==noreg)
return( 0 );
if( cum->p->params[cum->count] < 4 )
i = cum->p->params[cum->count]-1;
else
i = cum->p->params[cum->count]+4;
return( gen_rtx(REG, mode, i ));
}
rtx
function_value( tree amode, tree decl )
{
int i;
if ( (decl == NULL) || ((DECL_PARAM(decl) == NULL) || (DECL_PARAM(decl)->funcreturn==noreg)) )
if (TARGET_68881 && (GET_MODE_CLASS (TYPE_MODE (amode)) == MODE_FLOAT) )
return( gen_rtx(REG, TYPE_MODE (amode), 16 ) );
else
return( gen_rtx(REG, TYPE_MODE (amode), 0 ) );
else {
if( DECL_PARAM(decl)->funcreturn < pa0 )
i = DECL_PARAM(decl)->funcreturn-1;
else
i = DECL_PARAM(decl)->funcreturn+4;
return( gen_rtx(REG, TYPE_MODE (amode), i ));
}
}
extern int current_function_is_pascal;
rtx function_outgoing_value( tree amode, tree decl )
{
int i;
if( decl != NULL )
if ( (DECL_PARAM(decl) == NULL) || (DECL_PARAM(decl)->funcreturn==noreg) )
if( current_function_is_pascal ) {
return( gen_rtx (MEM, TYPE_MODE (amode),
gen_rtx (PLUS, Pmode,
frame_pointer_rtx,
gen_rtx (CONST_INT, VOIDmode, 12345))) );
}
else
return( function_value( amode, decl ) );
else {
if( DECL_PARAM(decl)->funcreturn < pa0 )
i = DECL_PARAM(decl)->funcreturn-1;
else
i = DECL_PARAM(decl)->funcreturn+4;
return( gen_rtx(REG, TYPE_MODE (amode), i ));
}
else
return( function_value( amode, decl ) );
}
function_prologue (fp, size)
FILE *fp;
int size;
{
register int regno;
char mask[100];
extern char call_used_regs[];
extern int frame_pointer_needed;
int fsize = ((size) + 3) & -4;
if (TARGET_FX30)
fprintf (fp, "m#start:\n");
mask[0] = '\0';
if (frame_pointer_needed)
{
if (TARGET_68020 || fsize < 0x8000)
fprintf (fp, "\tlink a6,#%d\n", -fsize);
else
fprintf (fp, "\tlink a6,#0\n\tsub.l #%d,sp\n", fsize);
}
for (regno = 16; regno < 24; regno++)
if (regs_ever_live[regno] && ! call_used_regs[regno])
{
strcat(mask, reg_names[regno]);
strcat(mask, "/");
}
if (strlen(mask) > 0)
{
mask[strlen(mask)-1] = '\0';
fprintf (fp, "\tfmovem %s,-(sp)\n", mask);
}
mask[0] = '\0';
for (regno = 0; regno < 16; regno++)
if (regs_ever_live[regno] && ! call_used_regs[regno] &&
!(frame_pointer_needed && regno == FRAME_POINTER_REGNUM))
{
strcat(mask, reg_names[regno]);
strcat(mask, "/");
}
if (TARGET_FX30)
{
strcat(mask, reg_names[13]);
strcat(mask, "/");
}
if (strlen(mask) > 0)
{
mask[strlen(mask)-1] = '\0';
if (index(mask, '/') == 0)
fprintf (fp, "\tmove.l %s,-(sp)\n", mask);
else
fprintf (fp, "\tmovem.l %s,-(sp)\n", mask);
}
if (TARGET_FX30)
{
fprintf (fp, "\tDC.L $4bfb0170 ; lea -> a5\n");
fprintf (fp, "\tDC.L _StaticDataArea-m#start-*+2\n");
}
/* Hook for tracing has to be called after everything else is set up. */
if (generate_trace_calls)
{
fprintf (fp, "\tIMPORT %%_BP\n");
fprintf (fp, "\tjsr %%_BP\n");
}
}
/* Function epilogue code restores any saved regs and then returns. There
are a number of obscure details and different options that can come up. */
/* Note the Macsbug symbol output at the end (see Appendix G of Macsbug
reference for details). */
/* Also note that the pascal function return sequence is cribbed from MPW,
for no particularly deep reason. */
function_epilogue (fp, size)
FILE *fp;
int size;
{
register int regno;
register int nregs;
int offset, foffset, fpoffset;
extern char call_used_regs[];
extern int current_function_pops_args;
extern int current_function_args_size;
extern int current_function_is_pascal;
extern int frame_pointer_needed;
int fsize = ((size) + 3) & -4;
int big = 0;
char mask[100], fmask[100];
/* Have to generate the trace calls after the return result is set up (by function
body), but before any state restoration happens. */
if (generate_trace_calls)
{
fprintf (fp, "\tmove.l d0,-(a7)\n");
fprintf (fp, "\tIMPORT %%_EP\n");
fprintf (fp, "\tjsr %%_EP\n");
fprintf (fp, "\tmove.l (a7)+,d0\n");
}
/* Now compute which of all the registers has to be restored. */
mask[0] = '\0'; fmask[0] = '\0';
nregs = 0;
for (regno = 16; regno < 24; regno++)
if (regs_ever_live[regno] && ! call_used_regs[regno])
{ nregs++; strcat(fmask, reg_names[regno]); strcat(fmask, "/"); }
foffset = nregs * 12;
nregs = 0;
if (frame_pointer_needed) regs_ever_live[FRAME_POINTER_REGNUM] = 0;
for (regno = 0; regno < 16; regno++)
if (regs_ever_live[regno] && ! call_used_regs[regno])
{ nregs++; strcat(mask, reg_names[regno]); strcat(mask, "/"); }
if (TARGET_FX30)
{ nregs++; strcat(mask, reg_names[13]); strcat(mask, "/"); }
offset = foffset + nregs * 4;
if (offset + fsize >= 0x8000
&& frame_pointer_needed
&& (strlen(mask) > 0 || strlen(fmask) > 0))
{ fprintf (fp, "\tmove.l #%d,a0\n", -fsize);
fsize = 0, big = 1; }
if (strlen(mask) > 0) {
mask[strlen(mask)-1] = '\0';
if (index(mask, '/') == 0) {
if (big)
fprintf (fp, "\tmove.l (-%d,a6,a0:l),%s\n", offset + fsize, mask);
else if (! frame_pointer_needed)
fprintf (fp, "\tmove.l (sp)+,%s\n", mask);
else
fprintf (fp, "\tmove.l -%d(a6),%s\n", offset + fsize, mask);
} else {
if (big)
fprintf (fp, "\tmovem.l (-%d,a6,a0.l),%s\n", offset + fsize, mask);
else if (! frame_pointer_needed)
fprintf (fp, "\tmovem.l (sp)+,%s\n", mask);
else
fprintf (fp, "\tmovem.l -%d(a6),%s\n", offset + fsize, mask);
}
}
if (strlen(fmask) > 0) {
fmask[strlen(fmask)-1] = '\0';
if (big)
fprintf (fp, "\tfmovem (-%d,a6,a0.l),%s\n", foffset + fsize, fmask);
else if (! frame_pointer_needed)
fprintf (fp, "\tfmovem (sp)+,%s\n", fmask);
else
fprintf (fp, "\tfmovem -%d(a6),%s\n", foffset + fsize, fmask);
}
if (frame_pointer_needed)
fprintf (fp, "\tunlk a6\n");
/* if (current_function_pops_args && current_function_args_size)
fprintf (fp, "\tDC.W $4e74, %d ; rtd\n",
current_function_args_size
+ (current_function_returns_struct ? 4 : 0));
else */ if (current_function_is_pascal && current_function_args_size > 0)
fprintf (fp, "\tmovea.l (sp)+,a0\n\tadd.w #%d,sp\n\tjmp (a0)\n",
current_function_args_size);
/* else if (current_function_returns_struct)
fprintf (fp, "\tDC.W $4e74, 4 ; rtd #4\n"); */
else
fprintf (fp, "\trts\n");
if (TARGET_MACSBUG)
{
extern char *current_function_name;
int len = strlen(current_function_name);
if (len < 32)
fprintf (fp, "\tDC.B $%x, '", 0x80 + len);
else
fprintf (fp, "\tDC.B $80, %d, '", len);
fprintf (fp, "%s", current_function_name);
fprintf (fp, "'%s\n", ((len < 32) ? ((len+1) & 1 ? ", 0" : "")
: ((len+2) & 1 ? ", 0" : "")));
}
/* Unconditional, so the "local data length" gets written properly */
dump_local_strings (fp);
}
char *
output_btst (operands, countop, dataop, insn, signpos)
rtx *operands;
rtx countop, dataop;
rtx insn;
int signpos;
{
operands[0] = countop;
operands[1] = dataop;
if (GET_CODE (countop) == CONST_INT)
{
register int count = INTVAL (countop);
if (count == signpos)
cc_status.flags = CC_NOT_POSITIVE | CC_Z_IN_NOT_N;
else
cc_status.flags = CC_NOT_NEGATIVE | CC_Z_IN_NOT_N;
if (count == 31
&& next_insns_test_no_inequality (insn))
return "tst.l %1";
if (count == 15
&& next_insns_test_no_inequality (insn))
return "tst.w %1";
if (count == 7
&& next_insns_test_no_inequality (insn))
return "tst.b %1";
cc_status.flags = CC_NOT_NEGATIVE;
}
return "btst %0,%1";
}
/* Return the best assembler insn template
for moving operands[1] into operands[0] as a fullword. */
static char *
singlemove_string (operands)
rtx *operands;
{
if (operands[1] != const0_rtx)
return "move.l %1,%0";
if (! ADDRESS_REG_P (operands[0]))
return "clr.l %0";
return "sub.l %0,%0";
}
/* Output assembler code to perform a doubleword move insn
with operands OPERANDS. */
char *
output_move_double (operands)
rtx *operands;
{
enum { REGOP, OFFSOP, MEMOP, PUSHOP, POPOP, CNSTOP, RNDOP } optype0, optype1;
rtx latehalf[2];
rtx addreg0 = 0, addreg1 = 0;
/* First classify both operands. */
if (REG_P (operands[0]))
optype0 = REGOP;
else if (offsettable_memref_p (operands[0]))
optype0 = OFFSOP;
else if (GET_CODE (XEXP (operands[0], 0)) == POST_INC)
optype0 = POPOP;
else if (GET_CODE (XEXP (operands[0], 0)) == PRE_DEC)
optype0 = PUSHOP;
else if (GET_CODE (operands[0]) == MEM)
optype0 = MEMOP;
else
optype0 = RNDOP;
if (REG_P (operands[1]))
optype1 = REGOP;
else if (CONSTANT_P (operands[1])
|| GET_CODE (operands[1]) == CONST_DOUBLE)
optype1 = CNSTOP;
else if (offsettable_memref_p (operands[1]))
optype1 = OFFSOP;
else if (GET_CODE (XEXP (operands[1], 0)) == POST_INC)
optype1 = POPOP;
else if (GET_CODE (XEXP (operands[1], 0)) == PRE_DEC)
optype1 = PUSHOP;
else if (GET_CODE (operands[1]) == MEM)
optype1 = MEMOP;
else
optype1 = RNDOP;
/* Check for the cases that the operand constraints are not
supposed to allow to happen. Abort if we get one,
because generating code for these cases is painful. */
if (optype0 == RNDOP || optype1 == RNDOP)
abort ();
/* If one operand is decrementing and one is incrementing
decrement the former register explicitly
and change that operand into ordinary indexing. */
if (optype0 == PUSHOP && optype1 == POPOP)
{
operands[0] = XEXP (XEXP (operands[0], 0), 0);
output_asm_insn ("subq.l #8,%0", operands);
operands[0] = gen_rtx (MEM, DImode, operands[0]);
optype0 = OFFSOP;
}
if (optype0 == POPOP && optype1 == PUSHOP)
{
operands[1] = XEXP (XEXP (operands[1], 0), 0);
output_asm_insn ("subq.l #8,%1", operands);
operands[1] = gen_rtx (MEM, DImode, operands[1]);
optype1 = OFFSOP;
}
/* If an operand is an unoffsettable memory ref, find a register
we can increment temporarily to make it refer to the second word. */
if (optype0 == MEMOP)
addreg0 = find_addr_reg (XEXP (operands[0], 0));
if (optype1 == MEMOP)
addreg1 = find_addr_reg (XEXP (operands[1], 0));
/* Ok, we can do one word at a time.
Normally we do the low-numbered word first,
but if either operand is autodecrementing then we
do the high-numbered word first.
In either case, set up in LATEHALF the operands to use
for the high-numbered word and in some cases alter the
operands in OPERANDS to be suitable for the low-numbered word. */
if (optype0 == REGOP)
latehalf[0] = gen_rtx (REG, SImode, REGNO (operands[0]) + 1);
else if (optype0 == OFFSOP)
latehalf[0] = adj_offsettable_operand (operands[0], 4);
else
latehalf[0] = operands[0];
if (optype1 == REGOP)
latehalf[1] = gen_rtx (REG, SImode, REGNO (operands[1]) + 1);
else if (optype1 == OFFSOP)
latehalf[1] = adj_offsettable_operand (operands[1], 4);
else if (optype1 == CNSTOP)
{
if (CONSTANT_P (operands[1]))
latehalf[1] = const0_rtx;
else if (GET_CODE (operands[1]) == CONST_DOUBLE)
{
union { REAL_VALUE_TYPE e; int i[3]; } ue;
union { double d; int i[2]; } ud;
ue.i[0] = CONST_DOUBLE_LOW (operands[1]);
ue.i[1] = CONST_DOUBLE_HIGH (operands[1]);
ue.i[2] = CONST_DOUBLE_TOP (operands[1]);
ud.d = ue.e;
latehalf[1] = gen_rtx (CONST_INT, VOIDmode, ud.i[1]);
operands[1] = gen_rtx (CONST_INT, VOIDmode, ud.i[0]);
}
}
else
latehalf[1] = operands[1];
/* If insn is effectively movd N(sp),-(sp) then we will do the
high word first. We should use the adjusted operand 1 (which is N+4(sp))
for the low word as well, to compensate for the first decrement of sp. */
if (optype0 == PUSHOP
&& REGNO (XEXP (XEXP (operands[0], 0), 0)) == STACK_POINTER_REGNUM
&& reg_overlap_mentioned_p (stack_pointer_rtx, operands[1]))
operands[1] = latehalf[1];
/* If one or both operands autodecrementing,
do the two words, high-numbered first. */
/* Likewise, the first move would clobber the source of the second one,
do them in the other order. This happens only for registers;
such overlap can't happen in memory unless the user explicitly
sets it up, and that is an undefined circumstance. */
if (optype0 == PUSHOP || optype1 == PUSHOP
|| (optype0 == REGOP && optype1 == REGOP
&& REGNO (operands[0]) == REGNO (latehalf[1])))
{
/* Make any unoffsettable addresses point at high-numbered word. */
if (addreg0)
output_asm_insn ("addql #4,%0", &addreg0);
if (addreg1)
output_asm_insn ("addql #4,%0", &addreg1);
/* Do that word. */
output_asm_insn (singlemove_string (latehalf), latehalf);
/* Undo the adds we just did. */
if (addreg0)
output_asm_insn ("subql #4,%0", &addreg0);
if (addreg1)
output_asm_insn ("subql #4,%0", &addreg1);
/* Do low-numbered word. */
output_asm_insn (singlemove_string (operands), operands);
return "";
}
/* Normal case: do the two words, low-numbered first. */
output_asm_insn (singlemove_string (operands), operands);
/* Make any unoffsettable addresses point at high-numbered word. */
if (addreg0)
output_asm_insn ("addql #4,%0", &addreg0);
if (addreg1)
output_asm_insn ("addql #4,%0", &addreg1);
/* Do that word. */
output_asm_insn (singlemove_string (latehalf), latehalf);
/* Undo the adds we just did. */
if (addreg0)
output_asm_insn ("subql #4,%0", &addreg0);
if (addreg1)
output_asm_insn ("subql #4,%0", &addreg1);
return "";
}
/* Output assembler code to perform a long double move insn
with operands OPERANDS. */
char *
output_move_extended (operands)
rtx *operands;
{
enum { REGOP, OFFSOP, MEMOP, PUSHOP, POPOP, CNSTOP, RNDOP } optype0, optype1;
rtx latehalf[2], thirdhalf[2];
rtx addreg0 = 0, addreg1 = 0;
/* First classify both operands. */
if (REG_P (operands[0]))
optype0 = REGOP;
else if (offsettable_memref_p (operands[0]))
optype0 = OFFSOP;
else if (GET_CODE (XEXP (operands[0], 0)) == POST_INC)
optype0 = POPOP;
else if (GET_CODE (XEXP (operands[0], 0)) == PRE_DEC)
optype0 = PUSHOP;
else if (GET_CODE (operands[0]) == MEM)
optype0 = MEMOP;
else
optype0 = RNDOP;
if (REG_P (operands[1]))
optype1 = REGOP;
else if (CONSTANT_P (operands[1])
|| GET_CODE (operands[1]) == CONST_DOUBLE)
optype1 = CNSTOP;
else if (offsettable_memref_p (operands[1]))
optype1 = OFFSOP;
else if (GET_CODE (XEXP (operands[1], 0)) == POST_INC)
optype1 = POPOP;
else if (GET_CODE (XEXP (operands[1], 0)) == PRE_DEC)
optype1 = PUSHOP;
else if (GET_CODE (operands[1]) == MEM)
optype1 = MEMOP;
else
optype1 = RNDOP;
/* Check for the cases that the operand constraints are not
supposed to allow to happen. Abort if we get one,
because generating code for these cases is painful. */
if (optype0 == RNDOP || optype1 == RNDOP || optype0 == MEMOP || optype1 == MEMOP)
abort ();
/* If one operand is decrementing and one is incrementing
decrement the former register explicitly
and change that operand into ordinary indexing. */
if (optype0 == PUSHOP && optype1 == POPOP)
{
operands[0] = XEXP (XEXP (operands[0], 0), 0);
output_asm_insn ("subq.l #12345,%0", operands);
operands[0] = gen_rtx (MEM, DImode, operands[0]);
optype0 = OFFSOP;
}
if (optype0 == POPOP && optype1 == PUSHOP)
{
operands[1] = XEXP (XEXP (operands[1], 0), 0);
output_asm_insn ("subq.l #12345,%1", operands);
operands[1] = gen_rtx (MEM, DImode, operands[1]);
optype1 = OFFSOP;
}
/* Ok, we can do one word at a time.
Normally we do the low-numbered word first,
but if either operand is autodecrementing then we
do the high-numbered word first.
In either case, set up in LATEHALF the operands to use
for the high-numbered word and in some cases alter the
operands in OPERANDS to be suitable for the low-numbered word. */
if (optype0 == REGOP)
latehalf[0] = gen_rtx (REG, SImode, REGNO (operands[0]) + 1);
else if (optype0 == OFFSOP)
latehalf[0] = adj_offsettable_operand (operands[0], (TARGET_68881 ? 4 :2));
else
latehalf[0] = operands[0];
if (optype1 == REGOP)
latehalf[1] = gen_rtx (REG, SImode, REGNO (operands[1]) + 1);
else if (optype1 == OFFSOP)
latehalf[1] = adj_offsettable_operand (operands[1], (TARGET_68881 ? 4 :2));
else if (optype1 == CNSTOP)
{
if (CONSTANT_P (operands[1]))
latehalf[1] = const0_rtx;
else if (GET_CODE (operands[1]) == CONST_DOUBLE)
{
latehalf[1] = gen_rtx (CONST_INT, VOIDmode,
CONST_DOUBLE_HIGH (operands[1]));
}
}
else
latehalf[1] = operands[1];
if (optype0 == REGOP)
thirdhalf[0] = gen_rtx (REG, SImode,
((!TARGET_68881 && REGNO (operands[0]) == 0) ?
8 :
REGNO (operands[0]) + 2));
else if (optype0 == OFFSOP)
thirdhalf[0] = adj_offsettable_operand (operands[0], (TARGET_68881 ? 8:6));
else
thirdhalf[0] = operands[0];
if (optype1 == REGOP)
thirdhalf[1] = gen_rtx (REG, SImode,
((!TARGET_68881 && REGNO (operands[1]) == 0) ?
8 :
REGNO (operands[1]) + 2));
else if (optype1 == OFFSOP)
thirdhalf[1] = adj_offsettable_operand (operands[1], (TARGET_68881 ? 8:6));
else if (optype1 == CNSTOP)
{
if (CONSTANT_P (operands[1]))
thirdhalf[1] = const0_rtx;
else if (GET_CODE (operands[1]) == CONST_DOUBLE)
{
thirdhalf[1] = gen_rtx (CONST_INT, VOIDmode,
CONST_DOUBLE_TOP (operands[1]));
operands[1] = gen_rtx (CONST_INT, VOIDmode,
(TARGET_68881 ? CONST_DOUBLE_LOW (operands[1])
: ((unsigned int) CONST_DOUBLE_LOW (operands[1])) >> 16));
}
}
else
thirdhalf[1] = operands[1];
/* If insn is effectively movd N(sp),-(sp) then we will do the
high word first. We should use the adjusted operand 1 (which is N+4(sp))
for the low word as well, to compensate for the first decrement of sp. */
if (optype0 == PUSHOP
&& REGNO (XEXP (XEXP (operands[0], 0), 0)) == STACK_POINTER_REGNUM
&& reg_overlap_mentioned_p (stack_pointer_rtx, operands[1]))
operands[1] = latehalf[1];
/* If one or both operands autodecrementing,
do the two words, high-numbered first. */
/* Likewise, the first move would clobber the source of the second one,
do them in the other order. This happens only for registers;
such overlap can't happen in memory unless the user explicitly
sets it up, and that is an undefined circumstance. */
if (optype0 == PUSHOP || optype1 == PUSHOP
|| (optype0 == REGOP && optype1 == REGOP
&& REGNO (operands[0]) == REGNO (latehalf[1])))
{
output_asm_insn (singlemove_string (thirdhalf), thirdhalf);
output_asm_insn (singlemove_string (latehalf), latehalf);
if (TARGET_68881)
output_asm_insn (singlemove_string (operands), operands);
else
output_asm_insn ("move.w %1,%0", operands);
return "";
}
/* Normal case: do the three words, low-numbered first. */
if (TARGET_68881)
output_asm_insn (singlemove_string (operands), operands);
else
output_asm_insn ("move.w %1,%0", operands);
output_asm_insn (singlemove_string (latehalf), latehalf);
output_asm_insn (singlemove_string (thirdhalf), thirdhalf);
return "";
}
/* Generate code for one of the extended-to-integer lib calls. The context
is such that we can't make call rtxes, and have to do it all manually. */
char *
output_lib_convert (operands, name)
rtx *operands;
char *name;
{
rtx aoperands[2];
char tmpbuf[1000];
int pushed = 0;
/* What regs need to be saved/restored? probably several... */
/* Set up a new operand array. */
aoperands[0] = gen_rtx (MEM, GET_MODE (aoperands[1]),
gen_rtx (PRE_DEC, Pmode, stack_pointer_rtx));
aoperands[1] = operands[1];
/* We need a pointer to the float; try different ways to get one. */
if (GET_CODE (aoperands[1]) == MEM)
{
output_asm_insn ("pea %1", aoperands);
}
else if (FP_REG_P (aoperands[1]))
{
output_asm_insn ("fmove.x %1,%0", aoperands);
output_asm_insn ("pea 0(sp)", NULL);
pushed = 1;
}
else
{
output_move_extended (aoperands);
output_asm_insn ("pea 0(sp)", NULL);
pushed = 1;
}
/* Do an on-the-spot importation of the libcall name. */
sprintf (tmpbuf, "IMPORT %s", name);
output_asm_insn (tmpbuf, NULL);
/* Now go and do it. */
if (TARGET_FX30)
sprintf (tmpbuf, "DC.W $61ff ; bsr.l\;DC.L %s-m#start-*", name);
else
sprintf (tmpbuf, "jsr %s", name);
output_asm_insn (tmpbuf, NULL);
/* If intended result place is not d0, move it there. */
if (! (REG_P (operands[0]) && REGNO (operands[0]) == 0))
{
aoperands[0] = operands[0];
aoperands[1] = gen_rtx (REG, SImode, 0);
output_asm_insn (singlemove_string (aoperands), aoperands);
}
/* Fix up the stack (we pushed a pointer and maybe an extended float
for it to point to). */
sprintf (tmpbuf, "add.w #%d,sp",
4 + (pushed ? mode_size[(int) XFmode] : 0));
output_asm_insn(tmpbuf, NULL);
return "";
}
/* The int library call convention is to use D0 and D1 with output to D0.
The complication here is that regalloc is already complete, so we have
to be very careful not to step on any important values. From md we
*are* guaranteed that output is a Dn, while input is either Dn or a
constant, which simplifies things considerably. */
/* (I'm not 100% convinced that this code is correct...) */
char *
output_int_lib_call (operands, name)
rtx *operands;
char *name;
{
char tmpbuf[1000];
int o0d0 = 0, o2d0 = 0, o0d1 = 0, o2d1 = 0, o0d2 = 0;
/* Int lib routines may step on a0. */
output_asm_insn ("movem.l a0/a1,-(sp)", NULL);
if (GET_CODE (operands[0]) == REG && REGNO (operands[0]) == 0) o0d0 = 1;
if (GET_CODE (operands[0]) == REG && REGNO (operands[0]) == 1) o0d1 = 1;
if (GET_CODE (operands[0]) == REG && REGNO (operands[0]) == 2) o0d2 = 1;
if (GET_CODE (operands[2]) == REG && REGNO (operands[2]) == 0) o2d0 = 1;
if (GET_CODE (operands[2]) == REG && REGNO (operands[2]) == 1) o2d1 = 1;
if (!o0d1)
output_asm_insn ("move.l d1,-(sp)", NULL);
if (!o0d2)
output_asm_insn ("move.l d2,-(sp)", NULL);
if (o0d0)
{
if (o2d1)
{
/* everything is exactly where we want it */
}
else
{
/* move operand 2 into d1. */
output_asm_insn ("move.l %2,d1", operands);
}
}
else
{
/* operand 0 is not in d0, so we need to save d0 on the stack before
copying the operand to d0. */
output_asm_insn ("move.l d0,-(sp)", NULL);
if (o0d1)
{
output_asm_insn ("move.l d1,d0", NULL);
if (o2d0)
{
/* Input and output are in exactly the opposite of where we want. */
/* d0 just got saved on the stack, so we can copy it from the stack
to d1. Note that we are copying the value not popping it off, so
that we can restore d0 later. */
output_asm_insn ("move.l (sp),d1", NULL);
}
else
{
/* move the input to d1. */
output_asm_insn ("move.l %2,d1", operands);
}
}
else
{
if (!o2d1)
{
/* operand 2 is not in d1, so we gotta move it to d1 */
output_asm_insn ("move.l %2,d1", operands);
}
/* operand 0 is not in either d0 or d1, so we gotta move it to d0 */
output_asm_insn ("move.l %0,d0", operands);
}
}
/* Do the int lib call proper now. */
sprintf (tmpbuf, "IMPORT %s", name);
output_asm_insn (tmpbuf, NULL);
if (TARGET_FX30)
sprintf (tmpbuf, "DC.W $61ff ; bsr.l\;DC.L %s-m#start-*", name);
else
sprintf (tmpbuf, "jsr %s", name);
output_asm_insn (tmpbuf, NULL);
/* Now clean up any mess we might have made on the stack, plus move the
result from d0 to wherever it was supposed to go. */
/* If operand 0 was not in d0, move the output to the correct place and then
restore d0 */
if (!o0d0)
{
output_asm_insn ("move.l d0,%0", operands);
output_asm_insn ("move.l (sp)+,d0", NULL);
}
if (!o0d2)
output_asm_insn ("move.l (sp)+,d2", NULL);
if (!o0d1)
output_asm_insn ("move.l (sp)+,d1", NULL);
/* restore a0, a1, d1, and d2 */
output_asm_insn ("movem.l (sp)+,a0/a1", NULL);
return "";
}
/* Helper stuff for SANE output routines. */
#define STKOFF(MODE,OFF) \
gen_rtx (MEM, (MODE), gen_rtx (PLUS, Pmode, stack_pointer_rtx, \
gen_rtx (CONST_INT, VOIDmode, (OFF))))
/* This takes a stack reference and changes it into a stack reference
4 bytes deeper. This is needed after a push on the stack. */
rtx deepen (x)
rtx x;
{
if (GET_CODE (x) == MEM
&& GET_CODE (XEXP (x, 0)) == PLUS
&& XEXP (XEXP (x, 0), 0) == stack_pointer_rtx)
{
return STKOFF (GET_MODE (x), 4 + INTVAL (XEXP (XEXP (x, 0), 1)));
}
else
return x;
}
/* Output a 2(1)-address operation that uses SANE. */
char *
output_sane_2 (opnds, op, name)
rtx *opnds;
int op;
char *name;
{
char tmpbuf[100], *cname;
enum machine_mode mode0 = GET_MODE (opnds[0]);
int space0 = 0, spacetmp = 0, space;
int cop;
rtx operands[2], moperands[2], coperands[2], tmprtx;
if (mode0 != XFmode)
spacetmp = GET_MODE_SIZE (XFmode);
if (REG_P (opnds[0]) || GET_CODE (opnds[0]) == CONST_DOUBLE)
space0 = GET_MODE_SIZE (GET_MODE (opnds[0]));
space = spacetmp + space0;
/* Reserve needed space for all our tmps and operands. */
if (spacetmp > 0)
{
sprintf(tmpbuf, "sub.w #%d,sp", spacetmp);
output_asm_insn(tmpbuf, NULL);
tmprtx = STKOFF (XFmode, space0);
}
if (space0 > 0)
{
output_sane_push(opnds[0]);
operands[0] = STKOFF (GET_MODE (opnds[0]), 0);
}
else
{
operands[0] = opnds[0];
}
/* Now everything is a pointer to memlocs where the numbers reside. */
/* First order of business is to make sure the input-output operand
is of extended type. If not, then we convert into a reserved place
and if so, we maybe put it on the stack anyway. */
if (mode0 != XFmode)
{
cop = (mode0 == HImode ? 0x200e :
(mode0 == SImode ? 0x280e :
(mode0 == SFmode ? 0x100e :
0x080e)));
cname = (mode0 == HImode ? "FI2X" :
(mode0 == SImode ? "FL2X" :
(mode0 == SFmode ? "FS2X" :
"FD2X")));
coperands[1] = operands[0];
coperands[0] = deepen (tmprtx);
output_sane_op (coperands, 2, cop, cname);
moperands[0] = tmprtx;
}
else
{
moperands[0] = operands[0];
}
/* Now we're all set to do the operation proper. */
output_sane_op (moperands, 1, op, name);
/* The result of the operation is still extended, so we may need to
do another conversion. Fortunately, the extended result is already in
memory, so we don't need to allocate, but the final result may need
to be sent back to regs. */
if (mode0 != XFmode)
{
cop = (mode0 == HImode ? 0x2010 :
(mode0 == SImode ? 0x2810 :
(mode0 == SFmode ? 0x1010 :
0x0810)));
cname = (mode0 == HImode ? "FX2I" :
(mode0 == SImode ? "FX2L" :
(mode0 == SFmode ? "FX2S" :
"FX2D")));
coperands[0] = deepen (operands[0]);
coperands[1] = moperands[0];
output_sane_op (coperands, 2, cop, cname);
}
/* SANE calls all done, now clean up. */
/* Pop the result if it had to be pushed originally. */
if (REG_P (opnds[0]) || GET_CODE (opnds[0]) == CONST_DOUBLE)
{
output_sane_pop (opnds[0]);
}
/* Deallocate everything else. */
if (spacetmp > 0)
{
sprintf (tmpbuf, "add.w #%d,sp", spacetmp);
output_asm_insn(tmpbuf, NULL);
}
/* All the code has already been dumped out. */
return "";
}
/* Output a compare operation that uses SANE (two float operands, integer
result). */
char *
output_sane_cmp (opnds, op, name)
rtx *opnds;
int op;
char *name;
{
char tmpbuf[100], *cname;
enum machine_mode mode0 = GET_MODE (opnds[0]);
enum machine_mode mode1 = GET_MODE (opnds[1]);
int space0 = 0, space1 = 0, spacetmp = 0, space;
int cop;
rtx operands[3], moperands[3], coperands[2], tmprtx, tmpop;
if (mode0 != XFmode)
spacetmp = GET_MODE_SIZE (XFmode);
if (REG_P (opnds[0]) || GET_CODE (opnds[0]) == CONST_DOUBLE)
space0 = GET_MODE_SIZE (GET_MODE (opnds[0]));
if (REG_P (opnds[1]) || GET_CODE (opnds[1]) == CONST_DOUBLE)
space1 = GET_MODE_SIZE (GET_MODE (opnds[1]));
space = spacetmp + space1 + space0;
/* Reserve needed space for all our tmps and operands. */
if (spacetmp > 0)
{
sprintf(tmpbuf, "sub.w #%d,sp", spacetmp);
output_asm_insn(tmpbuf, NULL);
tmprtx = STKOFF (XFmode, space0 + space1);
}
if (space1 > 0)
{
output_sane_push(opnds[1]);
operands[1] = STKOFF (GET_MODE (opnds[1]), space0);
}
else
{
operands[1] = opnds[1];
}
if (space0 > 0)
{
output_sane_push(opnds[0]);
operands[0] = STKOFF (GET_MODE (opnds[0]), 0);
}
else
{
operands[0] = opnds[0];
}
/* Now everything is a pointer to memlocs where the numbers reside. */
/* First order of business is to make sure the first operand
is of extended type. If not, then we convert into a reserved place
and if so, we maybe put it on the stack anyway. */
if (mode0 != XFmode)
{
cop = (mode0 == HImode ? 0x200e :
(mode0 == SImode ? 0x280e :
(mode0 == SFmode ? 0x100e :
0x080e)));
cname = (mode0 == HImode ? "FI2X" :
(mode0 == SImode ? "FL2X" :
(mode0 == SFmode ? "FS2X" :
"FD2X")));
coperands[1] = operands[0];
coperands[0] = deepen (tmprtx);
output_sane_op (coperands, 2, cop, cname);
moperands[0] = tmprtx;
}
else
{
moperands[0] = operands[0];
}
tmpop = moperands[0];
moperands[0] = deepen (moperands[0]);
moperands[1] = operands[1];
/* Now we're all set to do the comparison proper. */
output_sane_op (moperands, 2, op, name);
/* Deallocate everything else. Note that this add is really adda,
so condition codes coming back from SANE op are still valid. */
if (spacetmp + space1 + space0 > 0)
{
sprintf (tmpbuf, "add.w #%d,sp", spacetmp + space1 + space0);
output_asm_insn(tmpbuf, NULL);
}
/* All the code has already been dumped out. */
return "";
}
/* Output a compare against 0 by synthesizing the zero and calling cmp. */
char *
output_sane_tst (opnds, op, name)
rtx *opnds;
int op;
char *name;
{
enum machine_mode mode0 = GET_MODE (opnds[0]);
rtx operands[2];
operands[0] = opnds[0];
operands[1] = CONST0_RTX (mode0);
return output_sane_cmp (operands, op, name);
}
/* Output a conversion operation that uses SANE. This is sort of like
a two-address operation, except that conversions aren't needed... */
char *
output_sane_convert (opnds)
rtx *opnds;
{
char tmpbuf[100], *cname;
enum machine_mode frommode = GET_MODE (opnds[1]);
enum machine_mode tomode = GET_MODE (opnds[0]);
int space0 = 0, space1 = 0, spacetmp = 0, space;
int cop;
rtx operands[3], moperands[3], coperands[2], tmprtx, tmpop;
if (frommode == tomode) abort(); /* should never happen? */
if (frommode != XFmode && tomode != XFmode)
spacetmp = GET_MODE_SIZE (XFmode);
if (REG_P (opnds[0]) || GET_CODE (opnds[0]) == CONST_DOUBLE)
space0 = GET_MODE_SIZE (GET_MODE (opnds[0]));
if (REG_P (opnds[1]) || GET_CODE (opnds[1]) == CONST_DOUBLE)
space1 = GET_MODE_SIZE (GET_MODE (opnds[1]));
space = spacetmp + space1 + space0;
/* Reserve needed space for all our tmps and operands. */
if (spacetmp > 0)
{
sprintf(tmpbuf, "sub.w #%d,sp", spacetmp);
output_asm_insn(tmpbuf, NULL);
tmprtx = STKOFF (XFmode, space0 + space1);
}
if (space1 > 0)
{
output_sane_push(opnds[1]);
operands[1] = STKOFF (GET_MODE (opnds[1]), space0);
}
else
{
operands[1] = opnds[1];
}
if (space0 > 0)
{
output_sane_push(opnds[0]);
operands[0] = STKOFF (GET_MODE (opnds[0]), 0);
}
else
{
operands[0] = opnds[0];
}
if (frommode != XFmode)
{
cop = (frommode == HImode ? 0x200e :
(frommode == SImode ? 0x280e :
(frommode == SFmode ? 0x100e :
0x080e)));
cname = (frommode == HImode ? "FI2X" :
(frommode == SImode ? "FL2X" :
(frommode == SFmode ? "FS2X" :
"FD2X")));
coperands[1] = operands[1];
coperands[0] = deepen (tomode == XFmode ? operands[0] : tmprtx);
output_sane_op (coperands, 2, cop, cname);
}
/* If necessary, do the second conversion. */
if (tomode != XFmode)
{
cop = (tomode == HImode ? 0x2010 :
(tomode == SImode ? 0x2810 :
(tomode == SFmode ? 0x1010 :
0x0810)));
cname = (tomode == HImode ? "FX2I" :
(tomode == SImode ? "FX2L" :
(tomode == SFmode ? "FX2S" :
"FX2D")));
coperands[1] = (frommode == XFmode ? operands[1] : tmprtx);
coperands[0] = deepen (operands[0]);
output_sane_op (coperands, 2, cop, cname);
}
/* Pop the result if it wasn't memory-resident */
if (REG_P (opnds[0]) || GET_CODE (opnds[0]) == CONST_DOUBLE)
{
output_sane_pop (opnds[0]);
}
/* Finish cleaning up the stack. */
if (spacetmp + space1 > 0)
{
sprintf (tmpbuf, "add.w #%d,sp", spacetmp + space1);
output_asm_insn(tmpbuf, NULL);
}
return "";
}
/* Output a 3(2)-address operation that uses SANE. This routine will do
everything including type conversions, since this helps it use the
stack somewhat more efficiently. */
char *
output_sane_3 (opnds, op, name)
rtx *opnds;
int op;
char *name;
{
char tmpbuf[100], *cname;
enum machine_mode mode0 = GET_MODE (opnds[0]);
enum machine_mode mode2 = GET_MODE (opnds[2]);
int space0 = 0, space2 = 0, spacetmp = 0, space;
int cop;
rtx operands[3], moperands[3], coperands[2], tmprtx, tmpop;
if (mode0 != XFmode)
spacetmp = GET_MODE_SIZE (XFmode);
if (REG_P (opnds[0]) || GET_CODE (opnds[0]) == CONST_DOUBLE)
space0 = GET_MODE_SIZE (GET_MODE (opnds[0]));
if (REG_P (opnds[2]) || GET_CODE (opnds[2]) == CONST_DOUBLE)
space2 = GET_MODE_SIZE (GET_MODE (opnds[2]));
space = spacetmp + space2 + space0;
/* Reserve needed space for all our tmps and operands. */
if (spacetmp > 0)
{
sprintf(tmpbuf, "sub.w #%d,sp", spacetmp);
output_asm_insn(tmpbuf, NULL);
tmprtx = STKOFF (XFmode, space0 + space2);
}
if (space2 > 0)
{
output_sane_push(opnds[2]);
operands[2] = STKOFF (GET_MODE (opnds[2]), space0);
}
else
{
operands[2] = opnds[2];
}
if (space0 > 0)
{
output_sane_push(opnds[0]);
operands[0] = STKOFF (GET_MODE (opnds[0]), 0);
}
else
{
operands[0] = opnds[0];
}
/* Now everything is a pointer to memlocs where the numbers reside. */
/* First order of business is to make sure the input-output operand
is of extended type. If not, then we convert into a reserved place
and if so, we maybe put it on the stack anyway. */
if (mode0 != XFmode)
{
cop = (mode0 == HImode ? 0x200e :
(mode0 == SImode ? 0x280e :
(mode0 == SFmode ? 0x100e :
0x080e)));
cname = (mode0 == HImode ? "FI2X" :
(mode0 == SImode ? "FL2X" :
(mode0 == SFmode ? "FS2X" :
"FD2X")));
coperands[1] = operands[0];
coperands[0] = deepen (tmprtx);
output_sane_op (coperands, 2, cop, cname);
moperands[0] = tmprtx;
}
else
{
moperands[0] = operands[0];
}
tmpop = moperands[0];
moperands[0] = deepen (moperands[0]);
moperands[1] = operands[2];
/* Now we're all set to do the operation proper. */
output_sane_op (moperands, 2, op, name);
/* The result of the operation is still extended, so we may need to
do another conversion. Fortunately, the extended result is already in
memory, so we don't need to allocate, but the final result may need
to be sent back to regs. */
if (mode0 != XFmode)
{
cop = (mode0 == HImode ? 0x2010 :
(mode0 == SImode ? 0x2810 :
(mode0 == SFmode ? 0x1010 :
0x0810)));
cname = (mode0 == HImode ? "FX2I" :
(mode0 == SImode ? "FX2L" :
(mode0 == SFmode ? "FX2S" :
"FX2D")));
coperands[0] = deepen (operands[0]);
coperands[1] = tmpop;
output_sane_op (coperands, 2, cop, cname);
}
/* SANE calls all done, now clean up. */
/* Pop the result if it had to be pushed originally. */
if (REG_P (opnds[0]) || GET_CODE (opnds[0]) == CONST_DOUBLE)
{
output_sane_pop (opnds[0]);
}
/* Deallocate everything else. */
if (spacetmp + space2 > 0)
{
sprintf (tmpbuf, "add.w #%d,sp", spacetmp + space2);
output_asm_insn(tmpbuf, NULL);
}
/* All the code has already been dumped out. */
return "";
}
/* Push a SANE number onto the stack (usually from a reg). */
output_sane_push (x)
rtx x;
{
rtx pusherands[2];
pusherands[0] = gen_rtx (MEM, GET_MODE (x),
gen_rtx (PRE_DEC, Pmode, stack_pointer_rtx));
pusherands[1] = x;
switch (GET_MODE (x))
{
case HImode:
output_asm_insn ("move.w %1,%0", pusherands);
break;
case SImode:
case SFmode:
output_asm_insn ("move.l %f1,%0", pusherands);
break;
case DFmode:
output_move_double (pusherands);
break;
case XFmode:
output_move_extended (pusherands);
break;
default:
abort();
}
}
/* Pop a SANE number off the stack into a given place (most likely a reg). */
output_sane_pop (x)
rtx x;
{
rtx poperands[2];
poperands[0] = x;
poperands[1] = gen_rtx (MEM, GET_MODE (x),
gen_rtx (POST_INC, Pmode, stack_pointer_rtx));
switch (GET_MODE (x))
{
case HImode:
output_asm_insn ("move.w %1,%0", poperands);
break;
case SImode:
case SFmode:
output_asm_insn ("move.l %1,%0", poperands);
break;
case DFmode:
output_move_double (poperands);
break;
case XFmode:
output_move_extended (poperands);
break;
default:
abort();
}
}
/* Issue the basic SANE calling sequence; push addresses of operands (which
must already be in memory), push the op code, then trap. The trap
will pop everything itself, so no cleanup needed. */
output_sane_op (moperands, numopnds, op, name)
rtx *moperands;
int numopnds, op;
char *name;
{
char tmpbuf[100];
if (numopnds > 1)
output_asm_insn ("pea %1", moperands);
if (numopnds > 0)
output_asm_insn ("pea %0", moperands);
sprintf (tmpbuf, "move.w #$%x,-(sp) ; %s", op, name);
output_asm_insn (tmpbuf, NULL);
output_asm_insn ("dc.w $a9eb ; FP68K", NULL);
}
/* Return a REG that occurs in ADDR with coefficient 1.
ADDR can be effectively incremented by incrementing REG. */
static rtx
find_addr_reg (addr)
rtx addr;
{
while (GET_CODE (addr) == PLUS)
{
if (GET_CODE (XEXP (addr, 0)) == REG)
addr = XEXP (addr, 0);
else if (GET_CODE (XEXP (addr, 1)) == REG)
addr = XEXP (addr, 1);
else if (CONSTANT_P (XEXP (addr, 0)))
addr = XEXP (addr, 1);
else if (CONSTANT_P (XEXP (addr, 1)))
addr = XEXP (addr, 0);
else
abort ();
}
if (GET_CODE (addr) == REG)
return addr;
abort ();
}
char *
output_move_const_long_double (operands)
rtx *operands;
{
{
int code = standard_68881_constant_p (operands[1]);
if (code != 0)
{
static char buf[40];
sprintf (buf, "fmovecr #$%x,%%0", code & 0xff);
return buf;
}
return "fmove.x %1,%0";
}
}
char *
output_move_const_double (operands)
rtx *operands;
{
{
int code = standard_68881_constant_p (operands[1]);
if (code != 0)
{
static char buf[40];
sprintf (buf, "fmovecr #$%x,%%0", code & 0xff);
return buf;
}
return "fmove.d %1,%0";
}
}
char *
output_move_const_single (operands)
rtx *operands;
{
{
int code = standard_68881_constant_p (operands[1]);
if (code != 0)
{
static char buf[40];
sprintf (buf, "fmovecr #$%x,%%0", code & 0xff);
return buf;
}
return "fmove.s %1,%0";
}
}
/* Return nonzero if X, a CONST_DOUBLE, has a value that we can get
from the "fmovecr" instruction.
The value, anded with 0xff, gives the code to use in fmovecr
to get the desired constant. */
/* This seems dubious for MPW... */
int
standard_68881_constant_p (x)
rtx x;
{
union {double d; int i[2];} u;
register double d;
u.i[0] = CONST_DOUBLE_LOW (x);
u.i[1] = CONST_DOUBLE_HIGH (x);
d = u.d;
if (d == 0)
return 0x0f;
/* Note: there are various other constants available
but it is a nuisance to put in their values here. */
if (d == 1)
return 0x32;
if (d == 10)
return 0x33;
if (d == 100)
return 0x34;
if (d == 10000)
return 0x35;
if (d == 1e8)
return 0x36;
if (GET_MODE (x) == SFmode)
return 0;
if (d == 1e16)
return 0x37;
/* larger powers of ten in the constants ram are not used
because they are not equal to a `double' C constant. */
return 0;
}
/* Print out a string in MPW Asm-approved syntax. Main trickiness is
to recognize non-printing characters and put them out as raw bytes,
while keeping as many printable characters in a string as possible
(saves space in asm code and makes it more readable too). */
/* newline/cr character mapping was handled at lex time... */
output_mpw_string(fp, str, size)
FILE *fp;
char *str;
int size;
{
char ch;
int i;
fprintf(fp, "\tDC.B '");
for (i = 0; i < size; ++i)
{
ch = str[i];
if (ch == '\'')
{
fprintf(fp, "''");
}
else if ((ch >= '\0' && ch < ' ') || (ch > '~'))
{
fprintf(fp, "'\n\tDC.B %d\n\tDC.B '", ch);
}
else if (i > 0 && i % 60 == 0)
{
fprintf (fp, "%c'\n\tDC.B '", ch);
}
else
{
fprintf(fp, "%c", ch);
}
}
fprintf(fp, "'\n");
/* Ending NUL char is part of the string's "size", so doesn't need to be added. */
}
/* Print out a floating rtx in some useful way. */
#include "real.h"
output_mpw_float (fp, x)
FILE *fp;
rtx x;
{
union { REAL_VALUE_TYPE f; int i[3]; } u;
double d;
float f;
u.i[0] = CONST_DOUBLE_LOW (x);
u.i[1] = CONST_DOUBLE_HIGH (x);
u.i[2] = CONST_DOUBLE_TOP (x);
switch (GET_MODE (x))
{
case SFmode:
f = u.f;
fprintf (fp, "\"%.9g\"", f);
break;
case DFmode:
d = u.f;
fprintf (fp, "\"%.20g\"", d);
break;
case XFmode:
fprintf (fp, "\"%.30g\"", u.f);
break;
default:
abort ();
}
}
output_mpw_float_as_int (fp, x)
FILE *fp;
rtx x;
{
union { REAL_VALUE_TYPE f; int i[3]; } u;
union { float f; int i[1]; } u2;
u.i[0] = CONST_DOUBLE_LOW (x);
u.i[1] = CONST_DOUBLE_HIGH (x);
u.i[2] = CONST_DOUBLE_TOP (x);
u2.f = u.f;
switch (GET_MODE (x))
{
case SFmode:
fprintf (fp, "%d", u2.i[0]);
break;
case DFmode:
case XFmode:
fprintf (stderr, "Can't integerize a double or extended const!\n");
default:
abort ();
}
}
/* Printing extended floats is a little tricky, since both the compiler and
the target machines care about the 10/12-byte size. */
output_mpw_long_double (fp, x)
FILE *fp;
REAL_VALUE_TYPE x;
{
union { REAL_VALUE_TYPE f; short s[6]; } u;
int a = 0;
u.f = x;
fprintf (fp, "\tDC.W %d,", u.s[0]);
if (TARGET_68881)
{
#ifdef mc68881
fprintf (fp, "%d,", u.s[1]);
a = 1;
#else
fprintf (fp, "0,");
a = 0;
#endif
}
fprintf (fp, "%d,%d,%d,%d ; %.30g\n",
u.s[a+1], u.s[a+2], u.s[a+3], u.s[a+4], x);
}
/* We need to recognize all possible references to global data. */
global_data_ref_p (addr)
rtx addr;
{
switch (GET_CODE (addr))
{
case SYMBOL_REF:
{
char *name = XSTR (addr, 0);
return (name[0] == '2');
}
case PLUS:
return (global_data_ref_p (XEXP (addr, 0))
|| global_data_ref_p (XEXP (addr, 1)));
case CONST:
return global_data_ref_p (XEXP (addr, 0));
default:
return 0;
}
return 0;
}
/* Test a string to see if it matches any register names. A few extra
tests on individual chars to optimize - a more serious approach would
involve a binary search tree or something. Don't bother unless this
can be proved to take significant time! */
/* Names not in the regular reg_names list that the assembler knows about
and will choke on if you attempt to use them as labels. */
char *obscure_reg_names[] = {
"sp",
"a7",
"za7",
"pc",
"zpc",
"ccr",
"sr",
"usp",
"sfc",
"dfc",
"cacr",
"vbr",
"caar",
"msp",
"isp",
NULL };
mpw_register_name (str)
char *str;
{
int i;
extern char* reg_names[];
if (str[0] == 'd' || str[0] == 'a' || str[0] == 'f')
{
for (i = 0; i < FIRST_PSEUDO_REGISTER; ++i)
if (strcmp (str, reg_names[i]) == 0)
return 1;
}
if (str[0] == 'z' && (str[1] == 'd' || str[1] == 'a'))
{
for (i = 0; i < FIRST_PSEUDO_REGISTER; ++i)
if (strcmp (str, reg_names[i]+1) == 0)
return 1;
}
/* There are other more obscure register names to test as well. */
for (i = 0; obscure_reg_names[i]; ++i)
if (strcmp (str, obscure_reg_names[i]) == 0)
return 1;
return 0;
}
char *
avoid_mpw_register_name (name)
char *name;
{
int typechar = ('0' <= name[0] && name[0] <= '9');
char *newname = name;
if (mpw_register_name (name + typechar))
{
/* should warn that name is being changed. */
newname = (char *) xmalloc (strlen (name) + 2);
strcpy (newname, name);
strcat (newname, "_");
warning ("`%s' is a register name; changing to `%s'",
name + typechar, newname + typechar);
}
return newname;
}
/* Implementing -b requires saving up strings somewhere. */
char *lbls[100];
char *strs[100];
int lsix = 0;
int totlen = 0;
record_a_string(lbl, str)
char *lbl, *str;
{
if (lsix >= 99) abort ();
lbls[lsix] = lbl;
strs[lsix] = str;
totlen += strlen(str) + 1;
totlen += (totlen & 1); /* keeps it aligned */
lsix++;
}
/* Functions compiled with -b have all their strings at the end of the
function, preceded by a length field. */
/* Should scan a linked list of strings saved up for this function,
not a fixed-size array. */
dump_local_strings (fp)
FILE *fp;
{
int i;
/* Only really need the length word for debugging, otherwise skip it. */
if (TARGET_MACSBUG)
fprintf (fp, "\tDC.W %d\n", totlen);
for (i = 0; i < lsix; ++i)
{
/* String might be referred to from elsewhere within this file. */
fprintf (fp, "\tENTRY ");
assemble_name (fp, lbls[i]);
fprintf (fp, "\n");
assemble_name (fp, lbls[i]);
output_mpw_string (fp, strs[i], strlen(strs[i])+1);
fprintf (fp, "\tALIGN\n");
}
lsix = totlen = 0;
}